﻿using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Input;
using RegexTester.Helpers;
using RegexTester.Models;
using System;
using System.Windows;

namespace RegexTester
{
    public class ManPageVM : INotifyPropertyChanged
    {
        public ManPageVM()
        {
            CanEvaluate = true;
            CanOpenFile = true;
            CanSaveFile = true;
        }

        #region InputText
        public string InputText
        {
            get { return _InputText; }
            set
            {
                if (_InputText != value)
                {
                    _InputText = value;
                    OnPropertyChanged("InputText");
                }
            }
        }
        private string _InputText;
        #endregion

        #region RegexText
        [ValidateRegex]
        public string RegexText
        {
            get { return _RegexText; }
            set
            {
                if (_RegexText != value)
                {
                    Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "RegexText" });
                    
                    _RegexText = value;
                    OnPropertyChanged("RegexText");
                    OnPropertyChanged("CanShowResultCount");
                }
            }
        }
        private string _RegexText;
        #endregion

        #region Snippet
        public string Snippet
        {
            get { return _Snippet; }
            set
            {
                if (_Snippet != value)
                {
                    _Snippet = value;
                    OnPropertyChanged("Snippet");
                }
            }
        }
        private string _Snippet;
        #endregion

        #region MatchCollection
        public IEnumerable<RegexResult> MatchCollection
        {
            get { return _MatchCollection; }
            set
            {
                if (_MatchCollection != value)
                {
                    _MatchCollection = value;
                    OnPropertyChanged("MatchCollection");
                }
            }
        }
        private IEnumerable<RegexResult> _MatchCollection;

        #endregion


        #region MatchCount
        public int MatchCount
        {
            get { return _MatchCount; }
            set
            {
                if (_MatchCount != value)
                {
                    _MatchCount = value;
                    OnPropertyChanged("MatchCount");
                }
            }
        }
        private int _MatchCount;
        #endregion

        #region Duration
        public decimal Duration
        {
            get { return _Duration; }
            set
            {
                if (_Duration != value)
                {
                    _Duration = value;
                    OnPropertyChanged("Duration");
                }
            }
        }
        private decimal _Duration;
        #endregion

        #region CanShowResultCount
        public Visibility CanShowResultCount
        {
            get { return _CanShowResultCount; }
            set
            {
                if (_CanShowResultCount != value)
                {
                    _CanShowResultCount = value;
                    OnPropertyChanged("CanShowResultCount");
                }
            }
        }
        private Visibility _CanShowResultCount = Visibility.Collapsed;
        #endregion


        #region IgnoreCase
        public bool IgnoreCase
        {
            get { return _IgnoreCase; }
            set
            {
                if (_IgnoreCase != value)
                {
                    _IgnoreCase = value;
                    OnPropertyChanged("IgnoreCase");

                    Evaluate();
                }
            }
        }
        private bool _IgnoreCase;
        #endregion

        #region IsMultiLine
        public bool IsMultiLine
        {
            get { return _IsMultiLine; }
            set
            {
                if (_IsMultiLine != value)
                {
                    _IsMultiLine = value;
                    OnPropertyChanged("IsMultiLine");

                    //if (value)
                    //{
                    //    _IsSingleLine = false;
                    //    OnPropertyChanged("IsSingleLine");
                    //}

                    Evaluate();
                }
            }
        }
        private bool _IsMultiLine;
        #endregion

        #region IsSingleLine
        public bool IsSingleLine
        {
            get { return _IsSingleLine; }
            set
            {
                if (_IsSingleLine != value)
                {
                    _IsSingleLine = value;
                    OnPropertyChanged("IsSingleLine");

                    if (value)
                    {
                        //_IsMultiLine = false;
                        _IsECMAScript = false;
                        //OnPropertyChanged("IsMultiLine");
                        OnPropertyChanged("IsECMAScript");
                    }

                    Evaluate();
                }
            }
        }
        private bool _IsSingleLine;
        #endregion

        #region IsExplicitCapture
        public bool IsExplicitCapture
        {
            get { return _IsExplicitCapture; }
            set
            {
                if (_IsExplicitCapture != value)
                {
                    _IsExplicitCapture = value;
                    OnPropertyChanged("IsExplicitCapture");

                    if (value)
                    {
                        _IsECMAScript = false;
                        OnPropertyChanged("IsECMAScript");
                    }

                    Evaluate();
                }
            }
        }
        private bool _IsExplicitCapture;
        #endregion

        #region IgnorePatternWhitespace
        public bool IgnorePatternWhitespace
        {
            get { return _IgnorePatternWhitespace; }
            set
            {
                if (_IgnorePatternWhitespace != value)
                {
                    _IgnorePatternWhitespace = value;
                    OnPropertyChanged("IgnorePatternWhitespace");

                    if (value)
                    {
                        _IsECMAScript = false;
                        OnPropertyChanged("IsECMAScript");
                    }

                    Evaluate();
                }
            }
        }
        private bool _IgnorePatternWhitespace;
        #endregion

        #region IsECMAScript
        public bool IsECMAScript
        {
            get { return _IsECMAScript; }
            set
            {
                if (_IsECMAScript != value)
                {
                    _IsECMAScript = value;
                    OnPropertyChanged("IsECMAScript");

                    if (value)
                    {
                        this._IsSingleLine = false;
                        this._IsExplicitCapture = false;
                        this._IgnorePatternWhitespace = false;
                        this._IsRightToLeft = false;

                        OnPropertyChanged("IsSingleLine");
                        OnPropertyChanged("IsExplicitCapture");
                        OnPropertyChanged("IgnorePatternWhitespace");
                        OnPropertyChanged("IsRightToLeft");
                    }

                    Evaluate();
                }
            }
        }
        private bool _IsECMAScript;
        #endregion

        #region IsRightToLeft
        public bool IsRightToLeft
        {
            get { return _IsRightToLeft; }
            set
            {
                if (_IsRightToLeft != value)
                {
                    _IsRightToLeft = value;
                    OnPropertyChanged("IsRightToLeft");

                    if (value)
                    {
                        _IsECMAScript = false;
                        OnPropertyChanged("IsECMAScript");
                    }

                    Evaluate();
                }
            }
        }
        private bool _IsRightToLeft;
        #endregion


        #region Evaluate
        /// <summary>
        /// Executes the Evaluate command.  Supports command binding.
        /// </summary>
        public void Evaluate()
        {
            this.CanShowResultCount = CanEvaluate && !string.IsNullOrWhiteSpace(this.InputText)
                ? Visibility.Visible
                : Visibility.Collapsed;

            if (CanEvaluate)
            {
                RegexOptions options = RegexOptions.None;

                List<string> optionStrings = new List<string>();

                if (this.IgnoreCase)
                {
                    options |= RegexOptions.IgnoreCase;
                    optionStrings.Add("RegexOptions.IgnoreCase");
                }

                if (this.IsMultiLine)
                {
                    options |= RegexOptions.Multiline;
                    optionStrings.Add("RegexOptions.Multiline");
                }

                if (this.IsSingleLine)
                {
                    options |= RegexOptions.Singleline;
                    optionStrings.Add("RegexOptions.Singleline");
                }

                if (this.IsExplicitCapture)
                {
                    options |= RegexOptions.ExplicitCapture;
                    optionStrings.Add("RegexOptions.ExplicitCapture");
                }

                if (this.IgnorePatternWhitespace)
                {
                    options |= RegexOptions.IgnorePatternWhitespace;
                    optionStrings.Add("RegexOptions.IgnorePatternWhitespace");
                }

                if (this.IsECMAScript)
                {
                    options |= RegexOptions.ECMAScript;
                    optionStrings.Add("RegexOptions.ECMAScript");
                }

                if (this.IsRightToLeft)
                {
                    options |= RegexOptions.RightToLeft;
                    optionStrings.Add("RegexOptions.IgnoreCase");
                }

                string optionsString = optionStrings.Count == 0 ? "RegexOptions.None" : string.Join(" | ", optionStrings.ToArray());

                this.Snippet = string.Format(
                    "Regex regex = new Regex(\r\n\t@\"{0}\",\r\n\t{1}\r\n);",
                    RegexText.Replace("\"","\"\""),
                    optionsString);
                
                if (!string.IsNullOrWhiteSpace(this.InputText))
                {

                    string pattern = GetCleanRegexPattern(this.RegexText);

                    Regex r = new Regex(pattern, options);

                    DateTime start = DateTime.Now;
                    this.MatchCollection = GetRegexResults(r.Matches(this.InputText), r.GetGroupNames());
                    this.MatchCount = this.MatchCollection.Count();
                    this.Duration = ((decimal)(Convert.ToInt32(DateTime.Now.Subtract(start).TotalMilliseconds))) / 1000m;

                }
                else
                {
                    this.MatchCollection = null;
                }
            }
            else
            {
                this.MatchCollection = null;
            }

        }

        #region (CanEvaluate)
        /// <summary>
        /// Gets or sets a value indicating whether the Evaluate command can be called.
        /// Also determines whether a button bound to this command is enabled.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if the Evaluate command can be called; otherwise, <c>false</c>.
        /// </value>
        public bool CanEvaluate
        {
            get
            {
                // Note: Consider adding additional logic here.
                return
                    _CanEvaluate
                    //&& !string.IsNullOrWhiteSpace(this.InputText)
                    && !string.IsNullOrWhiteSpace(this.RegexText)
                    && Validator.TryValidateProperty(this.RegexText, new ValidationContext(this, null, null) { MemberName = "RegexText" }, null);
            }
            set
            {
                if (_CanEvaluate != value)
                {
                    _CanEvaluate = value;
                    OnPropertyChanged("CanEvaluate");
                }
            }
        }
        private bool _CanEvaluate;
        #endregion

        #region (EvaluateCommand)
        /// <summary>
        /// Gets or sets the Evaluate command.  Supports binding.
        /// </summary>
        public ICommand EvaluateCommand
        {
            get
            {
                return _EvaluateCommand;
            }
            set
            {
                if (_EvaluateCommand != value)
                {
                    _EvaluateCommand = value;
                    OnPropertyChanged("EvaluateCommand");
                }
            }
        }
        private ICommand _EvaluateCommand = new Helpers.Command { Method = "Evaluate" };
        #endregion

        #endregion

        #region OpenFile
        public void OpenFile()
        {
            if (CanOpenFile)
            {
                System.Windows.Controls.OpenFileDialog box = new System.Windows.Controls.OpenFileDialog();

                box.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";

                if (box.ShowDialog() == true)
                {
                    using (System.IO.Stream fileStream = box.File.OpenRead())
                    {
                        using (System.IO.StreamReader reader = new System.IO.StreamReader(fileStream))
                        {
                            this.RegexText = reader.ReadToEnd();
                        }
                        fileStream.Close();
                    }
                }
            }
        }

        #region (CanOpenFile)
        public bool CanOpenFile
        {
            get
            {
                // Note: Consider adding additional logic here.
                return _CanOpenFile;
            }
            set
            {
                if (_CanOpenFile != value)
                {
                    _CanOpenFile = value;
                    OnPropertyChanged("CanOpenFile");
                }
            }
        }
        private bool _CanOpenFile;
        #endregion

        #region (OpenFileCommand)
        public ICommand OpenFileCommand
        {
            get
            {
                return _OpenFileCommand;
            }
            set
            {
                if (_OpenFileCommand != value)
                {
                    _OpenFileCommand = value;
                    OnPropertyChanged("OpenFileCommand");
                }
            }
        }
        private ICommand _OpenFileCommand = new Helpers.Command { Method = "OpenFile" };
        #endregion
        #endregion

        #region SaveFile
        public void SaveFile()
        {
            if (CanSaveFile)
            {
                System.Windows.Controls.SaveFileDialog box = new System.Windows.Controls.SaveFileDialog();

                box.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";

                if (box.ShowDialog() == true)
                {
                    using (System.IO.Stream stream = box.OpenFile())
                    {
                        byte[] tbyte = (new System.Text.UTF8Encoding(true)).GetBytes(this.RegexText);
                        stream.Write(tbyte, 0, tbyte.Length);
                        stream.Close();
                    }
                }
            }
        }

        #region (CanSaveFile)
        public bool CanSaveFile
        {
            get
            {
                // Note: Consider adding additional logic here.
                return _CanSaveFile;
            }
            set
            {
                if (_CanSaveFile != value)
                {
                    _CanSaveFile = value;
                    OnPropertyChanged("CanSaveFile");
                }
            }
        }
        private bool _CanSaveFile;
        #endregion

        #region (SaveFileCommand)
        public ICommand SaveFileCommand
        {
            get
            {
                return _SaveFileCommand;
            }
            set
            {
                if (_SaveFileCommand != value)
                {
                    _SaveFileCommand = value;
                    OnPropertyChanged("SaveFileCommand");
                }
            }
        }
        private ICommand _SaveFileCommand = new Helpers.Command { Method = "SaveFile" };
        #endregion

        #endregion

        private string GetCleanRegexPattern(string pattern)
        {
            return
                string.Join(
                    Environment.NewLine,
                    from line in pattern.Split(new char[] { '\r', '\n' }, StringSplitOptions.None)
                    where !line.TrimStart().StartsWith("#")
                    select line);

            //string[] allLines = pattern.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

            //string[] lines =

            //    (from line in allLines
            //     where !line.TrimStart().StartsWith("#")
            //     select line)
            //    .ToArray();

            //string result = string.Join(Environment.NewLine, lines);

            //return result;

        }

        private IEnumerable<RegexResult> GetRegexResults(MatchCollection matchCollection, IEnumerable<string> groupNames)
        {
            foreach (Match match in matchCollection)
            {
                yield return new RegexResult
                {
                    Group=string.Empty,
                    Value=match.Value,
                    Index=match.Index,
                    Length=match.Length
                };

                foreach (string name in groupNames)
                {
                    if(match.Groups[name].Success && name!="0")
                        yield return new RegexResult
                        {
                            Group = name,
                            Value = match.Groups[name].Value,
                            Index = match.Groups[name].Index,
                            Length = match.Groups[name].Length
                        };
                }
            }
        }

        #region E:PropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, e);
        }
        protected virtual void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        } 
        #endregion
    }
}
